import { paths } from './api';

export type ApiPath = keyof paths;

type UnionToIntersection<T> = (T extends any ? (k: T) => void : never) extends (k: infer U) => void ? U : never;

export type HttpMethod = 'get' | 'post' | 'put' | 'delete'; // keyof UnionToIntersection<paths[keyof paths]>;

export type ExactHttpMethodByPath<Path extends ApiPath> = HttpMethod & keyof UnionToIntersection<paths[Path]>;

export type ExactPathByHttpMethod<Method extends HttpMethod> = Method extends any
  ? keyof {
      [K in keyof paths as paths[K] extends Record<Method, any> ? K : never]: paths[K];
    }
  : never;

type GetNestedValue<T extends Record<string, any>, Keys extends (string | number)[]> = 0 extends Keys['length']
  ? T
  : Keys extends [infer First, ...infer Rest]
  ? First extends keyof T
    ? Rest extends (string | number)[]
      ? GetNestedValue<Required<T>[First], Rest>
      : never
    : never
  : never;

type GetContent<Path extends ApiPath, Method extends HttpMethod, Code extends number> = GetNestedValue<
  paths,
  [Path, Method, 'responses', Code, 'content', 'application/json', 'data']
>;

export type ApiResponse<Method extends HttpMethod, Path extends ApiPath> = GetContent<Path, Method, 200 | 201>;

export const httpErrorStatusCodes = [400, 401, 404, 500] as const;

type HttpErrorCode = (typeof httpErrorStatusCodes)[number];

export type ApiErrorWithErrorCode<
  Path extends ApiPath,
  Method extends HttpMethod,
  Code extends HttpErrorCode
> = Code extends number
  ? GetContent<Path, Method, Code> extends never
    ? {
        status: Code;
        data: {
          message: string;
        };
      }
    : {
        status: Code;
        data: GetContent<Path, Method, Code>;
      }
  : never;

export type ApiError<Path extends ApiPath, Method extends HttpMethod> = ApiErrorWithErrorCode<Path, Method, HttpErrorCode>;

export type ApiPathParam<Method extends HttpMethod, Path extends ApiPath> = GetNestedValue<
  paths,
  [Path, Method, 'parameters', 'path']
>;

export type ApiQueryParam<Method extends HttpMethod, Path extends ApiPath> = GetNestedValue<
  paths,
  [Path, Method, 'parameters', 'query']
>;

export type ApiPathAndParam<Method extends HttpMethod, Path extends ApiPath> =
  | GetNestedValue<paths, [Path, Method, 'parameters', 'query']>
  | GetNestedValue<paths, [Path, Method, 'parameters', 'path']>;

export type ApiBody<Method extends HttpMethod, Path extends ApiPath> = GetNestedValue<
  paths,
  [Path, Method, 'requestBody', 'content', 'application/json']
>;

export type ApiPostBody<Path extends ApiPath> = GetNestedValue<
  paths,
  [Path, 'post', 'requestBody', 'content', 'application/json']
>;

export type ApiPutBody<Path extends ApiPath> = GetNestedValue<paths, [Path, 'put', 'requestBody', 'content', 'application/json']>;
